package top.jfunc.common.utils;

import java.util.Objects;
import java.util.function.Supplier;

/**
 * 保存内存值，超时获取时刷新
 * 适合用于在内存中缓存数据库数值，不必每次都去查询数据库
 * @author xiongshiyan
 * @param <T>
 */
public class MemoryRefresher<T> implements Supplier<T>{
    public static final long DEFAULT_REFRESH_TIMEOUT = 600_000L; // 10m

    private final Supplier<T> dataGetter;

    /**
     * 保存每次刷新的值，也就是你需要获取的值
     */
    private volatile T t;

    private volatile long lastUpdateAt = 0L;

    private boolean refreshEnabled = true;
    private long refreshTimeout = DEFAULT_REFRESH_TIMEOUT;

    public MemoryRefresher(Supplier<T> dataGetter) {
        this.dataGetter = Objects.requireNonNull(dataGetter);
    }
    public MemoryRefresher(long refreshTimeout, Supplier<T> dataGetter) {
        this.refreshTimeout = refreshTimeout;
        this.dataGetter = Objects.requireNonNull(dataGetter);
    }

    /**
     * 设置 刷新超时时间
     * @param refreshTimeout 刷新{@link #t} 的间隔时间
     */
    public MemoryRefresher<T> setRefreshTimeout(long refreshTimeout) {
        if (refreshTimeout < 0) {
            throw new IllegalArgumentException("超时时间不能小于0");
        }
        this.refreshTimeout = refreshTimeout;
        return this;
    }

    /**
     * 设置需不需要在{@link #refreshTimeout 一段时间}之后刷新状态
     */
    public MemoryRefresher<T> disableRefresh() {
        this.refreshEnabled = false;
        return this;
    }

    public T get() {
        if (needRefresh()) {
            synchronized(this) {
                if (needRefresh()) {
                    refresh();
                }
            }
        }
        return this.t;
    }

    /**
     * 版本兼容，提供老版本的方法，实际直接调用get
     */
    public T getData(){
        return get();
    }

    private boolean needRefresh() {
        return this.t == null
                || (refreshEnabled && System.currentTimeMillis() - lastUpdateAt >= refreshTimeout);
    }

    /**
     * 刷新缓存
     */
    public void refresh(){
        this.t = dataGetter.get();
        this.lastUpdateAt = System.currentTimeMillis();
    }
}
